#!/usr/bin/ruby

require 'xmmsclient'
require 'Qt'
require 'singleton'

include Math

class XmmsConnection < Xmms::Client
	include Singleton
	ClientName = 'MXC'
	def initialize
		super(ClientName)
		self.connect(ENV["XMMS_PATH"])
	end
end


class MXmms2Client < Qt::Splitter #'M' stands for Maniel's:>
	ClientId = 'MXC'
	VER = '0.0.0.1b'

	@@instance = nil

	signals 'trackChanged(int)', 'setLabText(const QString&)', 'updatePlayTime(const QString&)',
		'positionChanged(int)', 'updateSlider(int)' , 'setBand0Value(int)', 'setBand1Value(int)',
		'setBand2Value(int)', 'setBand3Value(int)', 'setBand4Value(int)', 'setBand5Value(int)',
		'setBand6Value(int)', 'setBand7Value(int)', 'setBand8Value(int)', 'setBand9Value(int)',
		'toggleEqualizer(bool)', 'setRVolume(int)', 'setLVolume(int)'

	slots	'on_read(int)', 'on_write(int)', 'stop()',
		'play_pause()','shuffle()', 'next_t()', 'prev_t()',
		'tick()', 'tack()', 'seekms(int)','seek()', 'onSliderReleased()',
		'onSliderPressed()', 'startSeekTimer()', 'stopSeekTimer()', 'setSeeked()',
		'popupMenu()', 'setBand0Val(int)', 'setBand1Val(int)',
		'setBand2Val(int)', 'setBand3Val(int)',
		'setBand4Val(int)', 'setBand5Val(int)',
		'setBand6Val(int)', 'setBand7Val(int)',
		'setBand8Val(int)', 'setBand9Val(int)',
		'toggleEq(bool)', 'setLVol(int)', 'setRVol(int)'
	
	attr_reader :xmms, :playbackstate, :nowplaying

	class << self
		def instance
			return @@instance
		end
	end

	def initialize(parent = nil, name = nil)
		return false if @@instance

		super
		@@instance = self

		setCaption("#{ClientId}")
		
		@playbackstate = nil
		@trackproperties = {}
		@nowplaying = 0
		@duration = 0
		@pltime = 0
		@pltimems = 0
		@prvseektime = 0
		@seeked = false
		@stream = false
		
		@xmms = XmmsConnection.instance

		addToQtEventLoop
		@xmms.on_disconnect {
			puts 'server exit, closing client'
			$app.exit
		}

		initGui
		connectGui

		handleIdChange { |id|
			@curid = id
			puts id
			if id!=0
				@trackproperties = @xmms.medialib_get_info(id).wait.value
				if @trackproperties[:chain] =~ /^curl/
					@positionslider.setValue(0)
					@positionslider.setEnabled(false)
					labtext = "%s\n%s"%[@trackproperties[:channel][0..40], @trackproperties[:title][0..40]]
					caption = "%s - %s - %s"%[ClientId,@trackproperties[:channel][0..40],@trackproperties[:title][0..40]]
				elsif @trackproperties[:chain] =~ /^file/ && @trackproperties[:title]
					@positionslider.setEnabled(true)
					@duration = @trackproperties[:duration]
					@positionslider.maxValue=@duration
					labtext = "%s\n%s"%[@trackproperties[:artist],@trackproperties[:title]]
					caption = "%s - %s - %s"%[ClientId,@trackproperties[:artist],@trackproperties[:title]]
				elsif @trackproperties == {}
					qDebug 'something\'s fucked up again:P'
				else
					@positionslider.setEnabled(true)
					@duration = @trackproperties[:duration]
					@positionslider.maxValue=@duration
					labtext = "%s"%[@trackproperties[:url].split('/')[-1].gsub('+',' ')]
					caption = "%s - %s"%[ClientId,@trackproperties[:url].split('/')[-1].gsub('+',' ')]
				end
				setCaption(caption)
				emit setLabText(labtext)
				emit trackChanged(id)
			else
				setCaption("#{ClientId}")
				emit setLabText('')
			end
		}

		

		handlePlaybackStatus { |state|
			@playbackstate = state
			case @playbackstate
				when Xmms::Client::PLAY
					@play.setPixmap(@pause_pxmap)
					@stop.enabled = true
					Qt::ToolTip.remove(@play)
					Qt::ToolTip.add(@play, 'Pause')
					@playlist.highlightCurrent(@nowplaying)
					if @trackproperties[:chain] =~ /^curl/
						@positionslider.setValue(0)
						@positionslider.setEnabled(false)
						caption = "%s - %s - %s"%[ClientId,@trackproperties[:channel][0..40],@trackproperties[:title][0..40]]
						qDebug 'playing internet stream, disabling slider'
					elsif @trackproperties[:chain] =~ /^file/ && @trackproperties[:title]
						@positionslider.setEnabled(true)
						caption = "%s - %s - %s"%[ClientId,@trackproperties[:artist],@trackproperties[:title]]
						qDebug 'playing a tagged file'
					elsif @trackproperties == {}
						qDebug "something's fucked up:)"
					else
						@positionslider.setEnabled(true)
						caption = "%s - %s"%[ClientId,@trackproperties[:url].split('/')[-1].gsub('+',' ')]
						qDebug 'playing an untagged file'
					end
				when Xmms::Client::STOP
					@playlist.pitemlist.each { |item|
						item.setPixmap(1, Qt::Pixmap.new)
					}
					@stop.enabled = false
					@play.setPixmap(@play_pxmap)
					Qt::ToolTip.remove(@play)
					Qt::ToolTip.add(@play, 'Play')
					@tlab.setText('')
					@positionslider.setValue(0)
					@positionslider.setEnabled(false)
					caption = "#{ClientId}"
				when Xmms::Client::PAUSE
					@playlist.pitemlist[@nowplaying].setPixmap(1, @pause_pxmap)
					@stop.enabled = false
					@play.setPixmap(@play_pxmap)
					Qt::ToolTip.remove(@play)
					Qt::ToolTip.add(@play, 'Play')
			end
			setCaption(caption)
		}

		@xmms.signal_playback_playtime.notifier { |res|
			begin
				pltime = res.value / 1000
				if @pltime != pltime
					@pltime = pltime
					@pltimems = res.value
					pltimestr = @pltime/3600 == 0 ? "%02d:%02d"%[(@pltime/ 60) % 60, @pltime % 60] :
						"%d:%02d:%02d"%[pltime/3600, (pltime/ 60) % 60, pltime % 60]
					emit updatePlayTime(pltimestr)
					if @trackproperties[:channel] == nil && !@positionslider.userChange
						emit updateSlider(res.value)
					end
				end
			rescue Xmms::Result::ValueError
				puts 'blah#1!:P'
			end
			res.restart
		}

		@xmms.playback_volume_get.notifier {|res|
			begin
				x = res.value
				emit setLVolume(x[:left].to_i)
				emit setRVolume(x[:right].to_i)
			rescue Xmms::Result::ValueError
				puts 'blah#2!:P'
			end
		}

		@xmms.broadcast_playback_volume_changed.notifier { |res|
			x = res.value
			emit setLVolume(x[:left].to_i)
			emit setRVolume(x[:right].to_i)
		}

		@xmms.configval_list.notifier {|res|
			x = res.value
			emit setBand0Value((x[:"equalizer.legacy0"].insert(0,'-').to_f*50).to_i)
			emit setBand1Value((x[:"equalizer.legacy1"].insert(0,'-').to_f*50).to_i)
			emit setBand2Value((x[:"equalizer.legacy2"].insert(0,'-').to_f*50).to_i)
			emit setBand3Value((x[:"equalizer.legacy3"].insert(0,'-').to_f*50).to_i)
			emit setBand4Value((x[:"equalizer.legacy4"].insert(0,'-').to_f*50).to_i)
			emit setBand5Value((x[:"equalizer.legacy5"].insert(0,'-').to_f*50).to_i)
			emit setBand6Value((x[:"equalizer.legacy6"].insert(0,'-').to_f*50).to_i)
			emit setBand7Value((x[:"equalizer.legacy7"].insert(0,'-').to_f*50).to_i)
			emit setBand8Value((x[:"equalizer.legacy8"].insert(0,'-').to_f*50).to_i)
			emit setBand9Value((x[:"equalizer.legacy9"].insert(0,'-').to_f*50).to_i)
			emit toggleEqualizer(x[:"equalizer.use_legacy"] == '1')
		}

		@xmms.broadcast_configval_changed.notifier { |res|
			x = res.value
			p x
			if x.has_key?:"equalizer.legacy0"
				emit setBand0Value((x[:"equalizer.legacy0"].insert(0,'-').to_f*50).to_i)
			elsif x.has_key?:"equalizer.legacy1"
				emit setBand1Value((x[:"equalizer.legacy1"].insert(0,'-').to_f*50).to_i)
			elsif x.has_key?:"equalizer.legacy2"
				emit setBand2Value((x[:"equalizer.legacy2"].insert(0,'-').to_f*50).to_i)
			elsif x.has_key?:"equalizer.legacy3"
				emit setBand3Value((x[:"equalizer.legacy3"].insert(0,'-').to_f*50).to_i)
			elsif x.has_key?:"equalizer.legacy4"
				emit setBand4Value((x[:"equalizer.legacy4"].insert(0,'-').to_f*50).to_i)
			elsif x.has_key?:"equalizer.legacy5"
				emit setBand5Value((x[:"equalizer.legacy5"].insert(0,'-').to_f*50).to_i)
			elsif x.has_key?:"equalizer.legacy6"
				emit setBand6Value((x[:"equalizer.legacy6"].insert(0,'-').to_f*50).to_i)
			elsif x.has_key?:"equalizer.legacy7"
				emit setBand7Value((x[:"equalizer.legacy7"].insert(0,'-').to_f*50).to_i)
			elsif x.has_key?:"equalizer.legacy8"
				emit setBand8Value((x[:"equalizer.legacy8"].insert(0,'-').to_f*50).to_i)
			elsif x.has_key?:"equalizer.legacy9"
				emit setBand9Value((x[:"equalizer.legacy9"].insert(0,'-').to_f*50).to_i)
			elsif x.has_key?:"equalizer.use_legacy"
				emit toggleEqualizer(x[:"equalizer.use_legacy"] == '1')
			end
		}

	end

	def setSeeked
		@seeked = false
	end

	def addToQtEventLoop
		@read=Qt::SocketNotifier.new(@xmms.io_fd, Qt::SocketNotifier::Read, self)
		connect(@read, SIGNAL('activated(int)'), SLOT('on_read(int)'))
		@read.setEnabled(false)

		@write=Qt::SocketNotifier.new(@xmms.io_fd, Qt::SocketNotifier::Write, self)
		connect(@write, SIGNAL('activated(int)'), SLOT('on_write(int)'))
		@write.setEnabled(false)
		
		@xmms.io_on_need_out {
			if @xmms.io_want_out
				@read.setEnabled(false)
				@write.setEnabled(true)
			else
				@read.setEnabled(true)
				@write.setEnabled(false)
			end
		}
	end

	def on_read(i)
		return if !@xmms.io_in_handle
	end

	def on_write(i)
		return if !@xmms.io_out_handle
	end

	def initGui
		@vbox1 = Qt::VBox.new(self)

		@cover = Qt::Pixmap.fromMimeSource($path+'/cover1.png')
		@coverlab = Qt::Label.new(@vbox1) { |coverlab|
			coverlab.setMinimumSize(250,250)
			coverlab.setSizePolicy(Qt::SizePolicy::Expanding, Qt::SizePolicy::Expanding)
			coverlab.setAlignment(AlignCenter | AlignVCenter)
			coverlab.setPixmap(@cover)
			coverlab.setFrameStyle(Qt::Frame::Panel | Qt::Frame::Plain)
			coverlab.setLineWidth(2)
		}

		@lab = Qt::Label.new(@vbox1) {
			setAlignment(AlignLeft | AlignTop | WordBreak)
			setSizePolicy(Qt::SizePolicy::Minimum, Qt::SizePolicy::Minimum)
			labfont = Qt::Font.new('Verdana', 14)
			labfont.setBold(true)
			setFont(labfont)
			setMinimumHeight(50)
		}

		@hbox0 = Qt::HBox.new(@vbox1)
		@llab = Qt::Label.new('L', @hbox0)
		@lvol = MxcSlider.new(Horizontal, @hbox0) {
			setRange(0,100)
			setFocusPolicy(Qt::Widget::NoFocus)
		}
		@rlab = Qt::Label.new('R', @hbox0)
		@rvol = MxcSlider.new(Horizontal, @hbox0) {
			setRange(0,100)
			setFocusPolicy(Qt::Widget::NoFocus)
		}

		@hbox1 = Qt::HBox.new(@vbox1) {
			setSpacing(5)
		}

		@positionslider = PositionSlider.new(Horizontal, @hbox1)

		@tlab = Qt::Label.new(@hbox1) {
			setAlignment(AlignVCenter | AlignRight)
			setSizePolicy(Qt::SizePolicy::Minimum, Qt::SizePolicy::Fixed)
		}
	
		@hbox2 = Qt::HBox.new(@vbox1){
			setSpacing(5)
		}
	
		@menubtn = Qt::PushButton.new(@hbox2) {
			setPixmap(Qt::Pixmap.fromMimeSource($path+'icons/player_volume.png'))
			setFixedSize(30, 30)
		}
		Qt::ToolTip.add(@menubtn, 'Click to show menu')

		@prv = Qt::PushButton.new(@hbox2) {
			setPixmap(Qt::Pixmap.fromMimeSource($path+'icons/player_rew.png'))
			setFixedHeight(30)
		}
		Qt::ToolTip.add(@prv, 'Previous')

		@play = Qt::PushButton.new(@hbox2) {
			setFixedHeight(30)
		}
		@play_pxmap = Qt::Pixmap.fromMimeSource($path+'icons/player_play.png')
		@pause_pxmap = Qt::Pixmap.fromMimeSource($path+'icons/player_pause.png')

		@stop = Qt::PushButton.new(@hbox2) {
			setPixmap(Qt::Pixmap.fromMimeSource($path+'icons/player_stop.png'))
			setFixedHeight(30)
		}
		Qt::ToolTip.add(@stop, 'Stop')

		@nxt = Qt::PushButton.new(@hbox2) {
			setPixmap(Qt::Pixmap.fromMimeSource($path+'icons/player_fwd.png'))
			setFixedHeight(30)
		}
		Qt::ToolTip.add(@nxt, 'Next')
		
		@shuffle = Qt::PushButton.new(@hbox2) {
			setPixmap(Qt::Pixmap.fromMimeSource($path+'icons/player_playlist.png'))
			setFixedHeight(30)
		}
		Qt::ToolTip.add(@shuffle, 'Shuffle')

		@menu = Qt::PopupMenu.new(self) { |menu|
			eqmenu = Qt::PopupMenu.new(menu)
			@equalizer = Equalizer.new(eqmenu)
			eqmenu.insertItem(@equalizer)

			menu.insertItem('Equalizer', eqmenu)
			menu.insertItem('MediaLib')
			menu.insertItem('Options')
		}

		@playlist = Playlist.new(self)

		setResizeMode(@vbox1, KeepSize)
	end

	def popupMenu
		@menu.popup(@menubtn.mapToGlobal(Qt::Point.new(0, 30)))
	end

	def connectGui
		connect(@prv, SIGNAL('clicked()'), SLOT('prev_t()'))
		connect(@play, SIGNAL('clicked()'), SLOT('play_pause()'))
		connect(@stop, SIGNAL('clicked()'), SLOT('stop()'))
		connect(@nxt, SIGNAL('clicked()'), SLOT('next_t()'))
		connect(@shuffle, SIGNAL('clicked()'), SLOT('shuffle()'))
		connect(@positionslider, SIGNAL('sliderMoved(int)'), SLOT('seekms(int)'))
		connect(@menubtn, SIGNAL('clicked()'), SLOT('popupMenu()'))
		connect(@rvol, SIGNAL('sliderMoved(int)'), SLOT('setRVol(int)'))
		connect(@lvol, SIGNAL('sliderMoved(int)'), SLOT('setLVol(int)'))

		connect(self, SIGNAL('updatePlayTime(const QString&)'), @tlab, SLOT('setText(const QString&)'))
		connect(self, SIGNAL('setLabText(const QString&)'), @lab, SLOT('setText(const QString&)'))
		connect(self, SIGNAL('updateSlider(int)'), @positionslider, SLOT('setValue(int)'))
		connect(self, SIGNAL('setRVolume(int)'), @rvol, SLOT('setValue(int)'))
		connect(self, SIGNAL('setLVolume(int)'), @lvol, SLOT('setValue(int)'))
	end


	def stop
		@xmms.playback_stop.notifier
	end

	def play_pause
		case @playbackstate
			when Xmms::Client::PLAY
				@xmms.playback_pause
			else
				@xmms.playback_start
		end
	end
	
	def next_t
		rel_jump(1)
	end

	def prev_t
		rel_jump(-1)
	end

	def rel_jump(where)
		@xmms.playlist_set_next_rel(where).wait
		@xmms.playback_tickle.wait
	end

	def shuffle
		@xmms.playlist_shuffle
	end

	def seekms(ms)
		@xmms.playback_seek_ms(ms)
		if @playbackstate == Xmms::Client::PAUSE
			@xmms.playback_start
		end
	end

	def handleIdChange
		id = @xmms.playback_current_id.wait.value
		yield(id)
		@xmms.broadcast_playback_current_id.notifier {|res|
			if id != res.value
				id = res.value
				yield(id)
			end
		}
	end

	def handlePositionChange
		res = @xmms.playlist_current_pos.wait
		begin
			@nowplaying = res.value
		rescue Xmms::Result::ValueError
			@nowplaying = -1
		end
		yield(@nowplaying)
		@xmms.broadcast_playlist_current_pos.notifier {|res|
			if @nowplaying != res.value
				@nowplaying = res.value
				yield(@nowplaying)
			end
		}
	end

	def handlePlaybackStatus
		state = @xmms.playback_status.wait.value
		yield(state)
		@xmms.broadcast_playback_status.notifier {|res|
			if state != res.value
				state = res.value
				yield(state)
			end
		}
	end

	def setRVol(i)
		@xmms.playback_volume_set('right', i)
	end

	def setLVol(i)
		@xmms.playback_volume_set('left', i)
	end

	def toggleEq(boolean)
		@xmms.configval_set('equalizer.use_legacy', boolean ? '1' : '0')
	end

	def setBand0Val(i)
		@xmms.configval_set('equalizer.legacy0', "#{i.abs/50.0}")
	end

	def setBand1Val(i)
		@xmms.configval_set('equalizer.legacy1', "#{i.abs/50.0}")
	end

	def setBand2Val(i)
		@xmms.configval_set('equalizer.legacy2', "#{i.abs/50.0}")
	end

	def setBand3Val(i)
		@xmms.configval_set('equalizer.legacy3', "#{i.abs/50.0}")
	end

	def setBand4Val(i)
		@xmms.configval_set('equalizer.legacy4', "#{i.abs/50.0}")
	end

	def setBand5Val(i)
		@xmms.configval_set('equalizer.legacy5', "#{i.abs/50.0}")
	end

	def setBand6Val(i)
		@xmms.configval_set('equalizer.legacy6', "#{i.abs/50.0}")
	end

	def setBand7Val(i)
		@xmms.configval_set('equalizer.legacy7', "#{i.abs/50.0}")
	end

	def setBand8Val(i)
		@xmms.configval_set('equalizer.legacy8', "#{i.abs/50.0}")
	end

	def setBand9Val(i)
		@xmms.configval_set('equalizer.legacy9', "#{i.abs/50.0}")
	end
end

module Kernel
	undef :format
end


if $0 == __FILE__
	exepath = File.expand_path(__FILE__)
	x=exepath.split('/')
	x.delete_at(x.length-1)
	$path = x.join('/')+'/'
	$: << $path
	$app = Qt::Application.new(ARGV)
	require 'playlist'
	require 'positionslider'
	require 'equalizer'
	trap('SIGINT') { $app.exit }
	$client = MXmms2Client.new
	$app.setMainWidget($client)
	$client.show
	$app.exec
end
